# Copyright (c) 2024 胡伯融
# 文件传输协议服务端 is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan
# PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#          http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
# KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
# NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
import os
import sys
import socket
import tkinter as tk

from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
from threading import Thread

# ：文件传输协议服务端被动模式。
# -类/FTPServerPassiveMode：-成员存储量/字符串/socket套接字-成员存储量/字符串/username用户名-成员存储量/字符串/password密码-成员存储量/路径/folder_path文件夹路径
class FTPServerPassiveMode:
    # ：构造函数。
    # -成员函数/__init__：-存储量/实例/self实例本身-存储量/字符串/param_socket套接字，-存储量/字符串/param_username用户名，-存储量/字符串/param_password密码，-存储量/路径/param_folder_path文件夹路径-返回无
    def __init__(self, param_socket: str="127.0.0.1", param_username: str="user", param_password: str="12345", param_folder_path: os.PathLike='.') -> None:
        self.socket = param_socket
        self.username = param_username
        self.password = param_password
        self.folder_path = param_folder_path

    # ：运行服务端和被动模式的文件传输协议。
    # -成员函数/start_protocol：-存储量/实例/self实例本身-返回无
    def start_protocol(self) -> None:
        authorizer = DummyAuthorizer()
        authorizer.add_user(self.username, self.password, self.folder_path, perm='elradfmw')
        handler = FTPHandler
        handler.passive_ports = range(60000, 65535)
        handler.masquerade_address = self.socket
        handler.authorizer = authorizer
        self.server = FTPServer(('0.0.0.0', 21), handler)
        self.server.serve_forever()

    # ：停止文件传输协议。
    # -成员函数/stop_protocol：-存储量/实例/self实例本身-返回无
    def stop_protocol(self) -> None:
        if self.server:
            self.server.close_all()


# ：文件传输协议服务端图形界面。
# ***tk.Lable：tkinter中的类，支持文本标签组件。
# ***tk.Entry：tkinter中的类，支持文本框组件。
# ***tk.Button：tkinter中的类，支持按钮组件，按钮允许调用。
# -类/FTPServerGUI：-成员存储量/FTPServerPassiveMode实例/server服务器-成员存储量/tk.TK实例/main_window主窗口-成员存储量/tk.Lable实例/socket_text套接字文本-成员存储量/tk.Entry实例/socket_textbox套接字文本框-成员存储量/tk.Label实例/username_text用户名字文本-成员存储量/tk.Entry实例/username_textbox用户名字文本框-成员存储量/tk.Lable实例/password_text密码文本-成员存储量/tk.Lable实例/folder_path_text文件夹路径文本-成员存储量/tk.Entry实例/folder_path_textbox文件夹路径文本框-成员存储量/tk.Button实例/start_button启动按钮-成员存储量/tk.Button实例/stop_button停止按钮
class FTPServerGUI:
    # ：构造函数。
    # *服务器位置：元组/字符串，两元素，分别表示IPv4套接字和共享文件夹路径。
    # -成员函数/__init__：-存储量/tk.Tk实例/param_main_window主窗口-存储量/元组/字符串/param_server_location服务器位置-返回无
    def __init__(self, param_main_window: tk.Tk, param_server_location: tuple[str]) -> None:
        self.server: FTPServerPassiveMode = None
        self.main_window = param_main_window
        self.main_window.title("FTP Server文件传输协议服务端")
        self.main_window.geometry("240x270")

        self.socket_text = tk.Label(param_main_window, text="Socket套接字：")
        self.socket_text.pack()
        self.socket_textbox = tk.Entry(param_main_window)
        self.socket_textbox.insert(0, param_server_location[0])
        self.socket_textbox.pack()

        self.username_text = tk.Label(param_main_window, text="Username用户名：")
        self.username_text.pack()
        self.username_textbox = tk.Entry(param_main_window)
        self.username_textbox.insert(0, "user")
        self.username_textbox.pack()

        self.password_text = tk.Label(param_main_window, text="Password密码：")
        self.password_text.pack()
        self.password_textbox = tk.Entry(param_main_window)
        self.password_textbox.insert(0, "12345")
        self.password_textbox.pack()

        self.folder_path_text = tk.Label(param_main_window, text="Folder Path文件夹路径：")
        self.folder_path_text.pack()
        self.folder_path_textbox = tk.Entry(param_main_window)
        self.folder_path_textbox.insert(0, param_server_location[1])
        self.folder_path_textbox.pack()

        self.start_button = tk.Button(param_main_window, text="Start Server运行服务端", command=self.start_server)
        self.start_button.pack()

        self.stop_button = tk.Button(param_main_window, text="Stop Server停止服务端", command=self.stop_server, state=tk.DISABLED)
        self.stop_button.pack()

    # ：运行服务端。
    # -成员函数/start_server：-self实例本身-返回无-线程实现
    def start_server(self) -> None:
        host = self.socket_textbox.get()
        user = self.username_textbox.get()
        password = self.password_textbox.get()
        directory = self.folder_path_textbox.get()

        self.server = FTPServerPassiveMode(host, user, password, directory)
        self.server_thread = Thread(target=self.server.start_protocol)
        self.server_thread.start()

        self.start_button.config(state=tk.DISABLED)
        self.stop_button.config(state=tk.NORMAL)

    # ：停止服务端。
    # -成员函数/stop_server：-self实例本身-返回无-线程实现
    def stop_server(self):
        if self.server:
            self.server.stop_protocol()
            self.server_thread.join()

        self.start_button.config(state=tk.NORMAL)
        self.stop_button.config(state=tk.DISABLED)


# ：面向当前手稿文件所在文件夹目录，创建共享物品们所在附属文件夹。
# -函数/create_folder_at_current_folder-返回共享文件夹路径
def create_subfolder_at_current_folder():
    current_file = os.path.abspath(__file__)
    current_folder = os.path.dirname(current_file)
    storage_folder = os.path.join(current_folder, 'FTP_files')
    os.makedirs(storage_folder, exist_ok=True)
    return storage_folder


# ：返回套接字。
# -函数/return_socket-返回套接字
def return_socket() -> str:
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.settimeout(0)
    try:
        s.connect(('8.8.8.8', 1))
        ip_address = str(s.getsockname()[0])
    except Exception:
        print("Network disconnected.")
        sys.exit()
    finally:
        s.close()
    return ip_address


# ：返回服务器抽象位置。
# -函数/initialize_environment-返回套接字和共享物品们所在文件夹路径
def return_location() -> tuple[str]:
    storage_folder = create_subfolder_at_current_folder()
    return return_socket(), storage_folder


# ：配置服务端参数，运行服务端应用。
# -函数/main-返回无
def main(server_location) -> None:
    main_window = tk.Tk()
    FTPServerGUI(main_window, server_location)
    main_window.mainloop()

if __name__ == "__main__":
    server_location = return_location()
    main(server_location)
